use pest::Parser;
use pest_derive::Parser;

#[derive(Parser)]
#[grammar = "sysy_lite.pest"]
pub struct SysYParser_lite2;

#[derive(Debug, PartialEq)]
pub struct Token {
    pub rule: String,  // 规则名
    pub value: String, // 符号值
    pub line: usize,   // 行号
}

pub fn tokenize(input: &str) -> Vec<Token> {
    //pest_ascii_tree::print_ascii_tree(Ok(SysYParser_lite::parse(Rule::CompUnit, input).unwrap()));

    let mut errs = Vec::new();
    let mut input_owned = input.to_string();
    let original_input = input.clone();
    let original_lines: Vec<String> = original_input.lines().map(|l| l.to_string()).collect();

    let mut tokens = Vec::new();
    let parse_result = SysYParser_lite2::parse(Rule::CompUnit, &input_owned);

    let pair = match parse_result {
        Ok(mut pairs) => pairs.next().unwrap(), // 取第一个匹配
        Err(e) => {
            let (line, col) = match e.line_col {
                pest::error::LineColLocation::Pos((line, col)) => (line, col),
                pest::error::LineColLocation::Span((line, col), _) => (line, col),
            };
            //let mut lines: Vec<String> = input_owned.lines().map(|l| l.to_string()).collect();
            errs.push(format!(
                "Error type A at Line {}: Col: {}, {}",
                line,
                col,
                "nothing"
                // original_lines
                //     .get(line.saturating_sub(1))
                //     .unwrap_or(&"".to_string())
            ));
            for err in errs {
                eprintln!("{}", err);
            }
            return tokens;
        }
    };

    pair.into_inner().for_each(|p| match p.as_rule() {
        Rule::CONST
        | Rule::INT
        | Rule::VOID
        | Rule::IF
        | Rule::ELSE
        | Rule::WHILE
        | Rule::BREAK
        | Rule::CONTINUE
        | Rule::RETURN
        | Rule::PLUS
        | Rule::MINUS
        | Rule::MUL
        | Rule::DIV
        | Rule::MOD
        | Rule::ASSIGN
        | Rule::EQ
        | Rule::NEQ
        | Rule::LT
        | Rule::GT
        | Rule::LE
        | Rule::GE
        | Rule::NOT
        | Rule::AND
        | Rule::OR
        | Rule::L_PAREN
        | Rule::R_PAREN
        | Rule::L_BRACE
        | Rule::R_BRACE
        | Rule::L_BRACKT
        | Rule::R_BRACKT
        | Rule::COMMA
        | Rule::SEMICOLON
        | Rule::IDENT
        | Rule::INTEGER_CONST => {
            
            let rule_name = format!("{:?}", p.as_rule());
            let mut value = p.as_str().to_string();
            let line = p.as_span().start_pos().line_col().0;

            if let Rule::INTEGER_CONST = p.as_rule() {
                value = if value.starts_with("0x") || value.starts_with("0X") {
                    // Hexadecimal
                    i64::from_str_radix(&value[2..], 16)
                        .map(|v| v.to_string())
                        .unwrap_or(value.clone())
                } else if value.starts_with("0") && value.len() > 1 {
                    // Octal
                    i64::from_str_radix(&value[1..], 8)
                        .map(|v| v.to_string())
                        .unwrap_or(value.clone())
                } else {
                    value.clone()
                };
            }
            println!("{} {} at Line {}.", rule_name, value, line);

            tokens.push(Token {
                rule: rule_name,
                value,
                line,
            });

        }
        _ => {
            //println!("{}", p);
            
            //unreachable!()
        },//return,
    });
    tokens
}
